home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-10-12 | 19.1 KB | 659 lines | [TEXT/CWIE] |
- unit WELineLayout;
-
- { WASTE PROJECT: }
- { Line Layout, Getting and Setting Variables, etc. }
-
- { Copyright © 1993-1994 Marco Piovanelli }
- { All Rights Reserved }
-
- interface
- uses
- WEDrawing;
-
- procedure WEStopInlineSession (hWE: WEHandle);
- function _WERemoveLine (lineIndex: LongInt;
- pWE: WEPtr): OSErr;
- function _WERecalBreaks (var startLine, endLine: LongInt;
- hWE: WEHandle): OSErr;
- procedure _WERecalSlops (firstLine, lastLine: LongInt;
- hWE: WEHandle);
- function WEGetTextLength (hWE: WEHandle): LongInt;
- function WECountLines (hWE: WEHandle): LongInt;
- function WEGetHeight (startLine, endLine: LongInt;
- hWE: WEHandle): LongInt;
- procedure WEGetSelection (var selStart, selEnd: LongInt;
- hWE: WEHandle);
- procedure WEGetDestRect (var destRect: LongRect;
- hWE: WEHandle);
- procedure WESetDestRect ({const} var destRect: LongRect;
- hWE: WEHandle);
- procedure WEGetViewRect (var viewRect: LongRect;
- hWE: WEHandle);
- procedure WESetViewRect ({const} var viewRect: LongRect;
- hWE: WEHandle);
- function WEGetText (hWE: WEHandle): Handle;
- function WEGetChar (offset: LongInt;
- hWE: WEHandle): Char;
- function WEGetAlignment (hWE: WEHandle): SignedByte;
- function WEUseText (text: Handle;
- hWE: WEHandle): OSErr;
-
- implementation
- uses
- FixMath;
-
- procedure WEStopInlineSession (hWE: WEHandle);
- var
- pWE: WEPtr;
- begin
- pWE := hWE^;
-
- { call FixTSMDocument only if the inline input area is actually "open" }
- if (pWE^.tsmAreaStart <> kInvalidOffset) then
- if (pWE^.tsmReference <> nil) then
- if (FixTSMDocument(pWE^.tsmReference) <> noErr) then
- ;
- end; { WEStopInlineSession }
-
- function _WERemoveLine (lineIndex: LongInt;
- pWE: WEPtr): OSErr;
-
- { remove the specified element from the line array }
-
- begin
-
- { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
- _WERemoveLine := _WERemoveSlot(pWE^.hLines, lineIndex, SizeOf(LineRec));
-
- { decrement line count }
- pWE^.nLines := pWE^.nLines - 1;
-
- end; { _WERemoveLine }
-
- function _WEInsertLine (lineIndex: LongInt;
- var theLine: LineRec;
- pWE: WEPtr): OSErr;
-
- { insert the specified element in the line array }
-
- var
- err: OSErr;
- begin
- _WEInsertLine := noErr;
-
- { do the insertion }
- err := _WEInsertSlot(pWE^.hLines, @theLine, lineIndex, SizeOf(theLine));
- if (err <> noErr) then
- begin
- _WEInsertLine := err;
- Exit(_WEInsertLine);
- end;
-
- { increment line count }
- pWE^.nLines := pWE^.nLines + 1;
-
- end; { _WEInsertLine }
-
- procedure _WEBumpOrigin (lineIndex: LongInt;
- deltaOrigin: LongInt;
- pWE: WEPtr);
- var
- pOrigin: LongIntPtr;
- nLines: LongInt;
- begin
- pOrigin := @pWE^.hLines^^[lineIndex].lineOrigin;
-
- { loop through the line run array adjusting the lineOrigin fields }
- nLines := pWE^.nLines;
- while (lineIndex <= nLines) do
- begin
- pOrigin^ := pOrigin^ + deltaOrigin;
- pOrigin := LongIntPtr(LongInt(pOrigin) + SizeOf(LineRec));
- lineIndex := lineIndex + 1;
- end;
- end; { _WEBumpOrigin }
-
- function _WEFindLineBreak (lineStart: LongInt;
- hWE: WEHandle): LongInt;
-
- { Find where to break the line beginning at lineStart }
- { the WE record and the text must be already locked }
- { the current graphics port must be already set up correctly }
-
- var
- pWE: WEPtr;
- pText: Ptr;
- offset, breakOffset: LongInt;
- textLength: LongInt;
- remainingLength: LongInt;
- segmentStart, segmentEnd: LongInt;
- runIndex: LongInt;
- runInfo: WERunInfo;
- pixelWidth: Fixed;
- script, previousScript: ScriptCode;
- isBreak: Boolean;
- begin
- pWE := hWE^;
- offset := lineStart;
- pText := Ptr(LongInt(pWE^.hText^) + offset);
- remainingLength := pWE^.textLength - offset;
-
- { find the style run index corresponding to the first segment on this line }
- runIndex := _WEOffsetToRun(offset, hWE);
-
- { initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity }
- pixelWidth := BSL(pWE^.destRect.right - pWE^.destRect.left, 16);
-
- { STYLE SEGMENT LOOP }
- repeat
-
- { get style run information for the current style run }
- _WEGetIndStyle(runIndex, runInfo, hWE);
- runIndex := runIndex + 1;
-
- { set text attributes in the graphics port }
- TextFont(runInfo.runAttrs.runStyle.tsFont);
- __TextFace(runInfo.runAttrs.runStyle.tsFace);
- TextSize(runInfo.runAttrs.runStyle.tsSize);
-
- { if we're handling multiscript text, keep track of script boundaries }
- if BTST(pWE^.flags, weFNonRoman) then
- begin
-
- { what is the script for this segment? }
- script := FontToScript(runInfo.runAttrs.runStyle.tsFont);
-
- { have we crossed a script run boundary in the middle of a line? }
- if (runInfo.runStart > offset) and (script <> previousScript) then
- begin
-
- { leave behind the all previous segments on this line }
- offset := runInfo.runStart;
- pText := Ptr(LongInt(pWE^.hText^) + offset);
- remainingLength := pWE^.textLength - offset;
- end;
-
- previousScript := script;
- end; { if non-Roman }
-
- { we'll pass textLength as the second parameter to the line break hook }
- { although this parameter is declared as a long, StyledLineBreak uses only }
- { the low word, so make sure it doesn't trespass the 32,767 byte threshold! }
- textLength := _WEPinInRange(remainingLength, 0, maxint);
-
- { calculate segmentStart and segmentEnd relative to offset }
- segmentStart := _WEPinInRange(runInfo.runStart - offset, 0, textLength);
- segmentEnd := _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
-
- { set breakOffset to a non-zero value for the first script run on the line, }
- { set it to zero for all subsequent script runs }
- breakOffset := Integer(offset = lineStart);
-
- if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
- begin
-
- { EMBEDDED OBJECT }
- { subtract object width from pixelWidth }
- pixelWidth := pixelWidth - BSL(WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.h, 16);
-
- { stop looping if pixelWidth has gone negative }
- isBreak := (pixelWidth < 0);
-
- if (isBreak) then
- breakOffset := segmentStart { break line before the object }
- else
- breakOffset := segmentEnd; { break line after the object }
-
- end
- else
- begin
-
- { REGULAR TEXT }
-
- {$IFC WASTE_DEBUG}
- _WEAssert(pWE^.lineBreakHook <> nil, 'Missing LineBreak Hook');
- {$ENDC}
-
- { let the line break hook do the work for us }
- isBreak := (CallWELineBreakProc(pText, textLength, segmentStart, segmentEnd, pixelWidth, breakOffset, hWE, pWE^.lineBreakHook) <> smBreakOverflow);
-
- end;
-
- { break the line anyway when we reach the end of the text }
- if (segmentEnd >= remainingLength) then
- isBreak := true;
-
- until (isBreak);
-
- { return the offset from lineStart to the break point }
- _WEFindLineBreak := (offset - lineStart) + breakOffset;
-
- end; { _WEFindLineBreak }
-
- procedure _WECalcHeights (rangeStart, rangeEnd: LongInt;
- var lineAscent, lineDescent: Integer;
- hWE: WEHandle);
-
- { Find the maximum ascent and descent values between rangeStart and rangeEnd }
- { the WE record must be already locked }
- { the current graphics port must be already set up correctly }
-
- var
- runIndex: LongInt;
- runInfo: WERunInfo;
- runAscent, runDescent: Integer;
- begin
- lineAscent := 1;
- lineDescent := 1;
-
- { find the style run index corresponding to the first segment on this line }
- runIndex := _WEOffsetToRun(rangeStart, hWE);
-
- { STYLE SEGMENT LOOP }
- repeat
-
- { get style run information for the current style run }
- _WEGetIndStyle(runIndex, runInfo, hWE);
- runIndex := runIndex + 1;
-
- { calculate ascent and descent (actually, descent + leading) values for this style run }
- if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
- begin
-
- { EMBEDDED OBJECT }
- runAscent := WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.v;
- runDescent := 0;
- end
- else
- begin
-
- { REGULAR TEXT }
- runAscent := runInfo.runAttrs.runAscent;
- runDescent := runInfo.runAttrs.runHeight - runAscent;
- end;
-
- { save the maximum values in lineAscent and lineDescent }
- if (runAscent > lineAscent) then
- lineAscent := runAscent;
- if (runDescent > lineDescent) then
- lineDescent := runDescent;
-
- { keep looping until we reach rangeEnd }
- until (runInfo.runEnd >= rangeEnd);
-
- end; { _WECalcHeights }
-
- function _WERecalBreaks (var startLine, endLine: LongInt;
- hWE: WEHandle): OSErr;
-
- { Recalculates line breaks, line heights and ascents for all the text or for a portion of it. }
- { On entry, startLine and endLine define a range of lines to recalculate. }
- { On exit, startLine to endLine defines the range of lines actually recalculated }
- { the WE record must already be locked }
-
- label
- 1;
- var
- pWE: WEPtr;
- pLine: LinePtr;
- lineInfo, oldLineInfo: LineRec;
- lineIndex: LongInt;
- recalThreshold: LongInt;
- lineOffset: LongInt;
- lineAscent, lineDescent: Integer;
- textHeight: LongInt;
- saveTextLock: Boolean;
- saveEnvironment: QDEnvironment;
- err: OSErr;
- begin
- _WERecalBreaks := noErr;
- pWE := hWE^;
-
- { lock the text }
- saveTextLock := _WESetHandleLock(pWE^.hText, true);
-
- { find the character offset that must be necessarily reached before we can }
- { even consider the possibility of stopping the recalculation process }
- { this offset, recalThreshold, is the last character on endLine _before_ recalculation }
- lineIndex := _WEPinInRange(endLine, 0, pWE^.nLines - 1);
- recalThreshold := pWE^.hLines^^[lineIndex + 1].lineStart;
-
- { we start recalculating line breaks from the line actually _preceding_ startLine, }
- { since editing startLine may cause part of its text to fit on the preceding line }
- lineIndex := _WEPinInRange(startLine - 1, 0, pWE^.nLines - 1);
-
- { find where in the text recalculation should begin }
- lineInfo := pWE^.hLines^^[lineIndex];
-
- { save the QuickDraw environment }
- _WESaveQDEnvironment(pWE^.port, false, saveEnvironment);
-
- { MAIN LINE BREAKING LOOP }
- repeat
-
- { find where to break the current line }
- lineOffset := _WEFindLineBreak(lineInfo.lineStart, hWE);
-
- { make sure we advance at least by one character (unless we reached the end of text) }
- if (lineOffset <= 0) then
- if (lineInfo.lineStart < pWE^.textLength) then
- lineOffset := 1;
-
- { calculate ascent and descent values for this line }
- _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, lineAscent, lineDescent, hWE);
-
- { save the maximum line ascent for this line in the line array }
- pLine := @pWE^.hLines^^[lineIndex];
- pLine^.lineAscent := lineAscent;
-
- { increment counters (go to the next line array entry) }
- lineIndex := lineIndex + 1;
- lineInfo.lineStart := lineInfo.lineStart + lineOffset;
- lineInfo.lineOrigin := lineInfo.lineOrigin + (lineAscent + lineDescent);
- pLine := LinePtr(LongInt(pLine) + SizeOf(LineRec));
-
- { compare the newly calculated line start with the old value }
- { if the new line start comes before the old line start, insert a new element }
- oldLineInfo := pLine^;
- if (lineIndex > pWE^.nLines) | (lineInfo.lineStart < oldLineInfo.lineStart) then
- begin
- err := _WEInsertLine(lineIndex, lineInfo, pWE);
-
- { clean up and exit if we ran out of memory }
- if (err <> noErr) then
- begin
- _WERecalBreaks := err;
- goto 1;
- end;
- end
- else
- begin
-
- { overwrite the old element }
- pLine^.lineStart := lineInfo.lineStart;
- pLine^.lineOrigin := lineInfo.lineOrigin;
-
- { remove all further elements which have a lineStart field }
- { less than or equal to the current one }
- while (lineIndex + 1 <= pWE^.nLines) & (lineInfo.lineStart >= pWE^.hLines^^[lineIndex + 1].lineStart) do
- err := _WERemoveLine(lineIndex + 1, pWE);
-
- { if the new line start is the same as the old one... }
- if (lineInfo.lineStart = oldLineInfo.lineStart) then
- begin
-
- { ...and recalThreshold has been reached, we can stop recalculating line breaks }
- if (lineInfo.lineStart >= recalThreshold) then
- begin
- { although line breaks need not be changed from lineIndex on, }
- { the lineOrigin fields may need to be changed }
- if (lineInfo.lineOrigin <> oldLineInfo.lineOrigin) then
- _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
-
- { exit from the line breaking loop }
- goto 1;
- end;
- end
- else
- begin
-
- { otherwise, the new line start comes after the old line start... }
- { if the current line is the one preceding startLine, warn our caller about this }
- if ((lineIndex > 0) and (lineIndex = startLine)) then
- startLine := lineIndex - 1;
-
- end;
- end;
-
- until (lineInfo.lineStart >= pWE^.textLength);
-
- 1:
- { calculate total text height }
- textHeight := WEGetHeight(0, pWE^.nLines, hWE);
-
- { quirk: if the last character in the text is a carriage return, the caret appears }
- { below the last line, so in this case we need to add the extra height to textHeight }
- if (WEGetChar(pWE^.textLength - 1, hWE) = CHR(13)) then
- textHeight := textHeight + WEGetHeight(pWE^.nLines - 1, pWE^.nLines, hWE);
-
- { if total text height has changed, remember to call the scroll callback later }
- if (textHeight <> pWE^.destRect.bottom - pWE^.destRect.top) then
- BSET(pWE^.flags, weFDestRectChanged);
-
- { set destRect.bottom to destRect.top + total text height }
- pWE^.destRect.bottom := pWE^.destRect.top + textHeight;
-
- { return through endLine the index of the last line affected by recalculation }
- endLine := lineIndex - 1;
-
- { make sure startLine isn't greater than endLine }
- if (startLine > endLine) then
- startLine := endLine;
-
- { unlock the text }
- if (_WESetHandleLock(pWE^.hText, saveTextLock)) then
- ;
-
- { restore the QuickDraw environment }
- _WERestoreQDEnvironment(saveEnvironment);
-
- end; { _WERecalBreaks }
-
- procedure _WERecalSlops (firstLine, lastLine: LongInt;
- hWE: WEHandle);
-
- { Calculates the lineSlop and lineJustAmount fields }
- { of the line array for the specified lines }
-
- var
- pWE: WEPtr;
- textLength: LongInt;
- totalSlop, lineWidth: Integer;
- totalProportion: Fixed;
-
- function SLCalcSlop (pLine: LinePtr;
- pAttrs: WERunAttributesPtr;
- pSegment: Ptr;
- segmentStart, segmentLength: LongInt;
- styleRunPosition: JustStyleCode): Boolean;
- var
- segmentWidth: Integer;
- segmentProportion: Fixed;
- isEndOfLine: Boolean;
- begin
- SLCalcSlop := false; { keep looping }
-
- { see if this text segment ends with a carriage return, or if we've reached the }
- { end of the text (in which case we don't want any justification to take place) }
- isEndOfLine := (segmentStart + segmentLength >= textLength) | (Ptr(LongInt(pSegment) + segmentLength - 1)^ = kEOL);
-
- { if this is the first segment on the line, reset line totals }
- if (styleRunPosition <= leftStyleRun) then
- begin
- totalSlop := lineWidth;
- totalProportion := 0;
- end;
-
- if (pAttrs^.runStyle.tsObject <> kNullObject) then
- begin
-
- { EMBEDDED OBJECT }
- { segment width is just object width; no extra space can be applied for justification }
- segmentWidth := WEObjectDescHandle(pAttrs^.runStyle.tsObject)^^.objectSize.h;
- segmentProportion := 0;
- end
- else
- begin
-
- { REGULAR TEXT }
- { if this is the last segment on the line, strip trailing spaces }
- if (not Odd(styleRunPosition)) then
- segmentLength := VisibleLength(pSegment, segmentLength);
-
- { measure this segment }
- segmentWidth := TextWidth(pSegment, 0, segmentLength);
-
- { calculate the proportion of extra space to apply to this text segment }
- segmentProportion := PortionLine(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling));
- end;
-
- { keep track of line totals }
- totalSlop := totalSlop - segmentWidth;
- totalProportion := totalProportion + segmentProportion;
-
- { if this is the last segment on the line, save values in the line array }
- if (not Odd(styleRunPosition)) then
- begin
-
- { make sure slop is non-negative }
- if (totalSlop < 0) then
- totalSlop := 0;
- pLine^.lineSlop := totalSlop;
-
- if (isEndOfLine) then
- pLine^.lineJustAmount := 0
- else
- pLine^.lineJustAmount := FixDiv(BSL(totalSlop, 16), totalProportion);
-
- end;
- end; { SLCalcSlop }
-
- begin
- pWE := hWE^;
-
- { don't bother recalculating slops if the text is left-aligned }
- if (pWE^.alignment = weFlushLeft) then
- Exit(_WERecalSlops);
-
- textLength := pWE^.textLength;
- lineWidth := pWE^.destRect.right - pWE^.destRect.left;
-
- { calculate slop and normalized slop proportion for all lines }
- _WESegmentLoop(firstLine, lastLine, SLCalcSlop, hWE);
-
- end; { _WERecalSlops }
-
- function WEUseText (text: Handle;
- hWE: WEHandle): OSErr;
- var
- pWE: WEPtr;
- textLength: LongInt;
- saveWELock: Boolean;
- begin
- WEUseText := noErr;
-
- { lock the WE record }
- saveWELock := _WESetHandleLock(hWE, true);
- pWE := hWE^;
-
- { install the text }
- _WEForgetHandle(pWE^.hText);
- pWE^.hText := text;
- textLength := GetHandleSize(text);
- pWE^.textLength := textLength;
- pWE^.hRuns^^[pWE^.nRuns].runStart := textLength + 1;
- pWE^.hLines^^[pWE^.nLines].lineStart := textLength;
-
- { unlock the WE record }
- if (_WESetHandleLock(hWE, saveWELock)) then
- ;
-
- end; { WEUseText }
-
- function WEGetAlignment (hWE: WEHandle): SignedByte;
- begin
- WEGetAlignment := hWE^^.alignment;
- end; { WEGetAlignment }
-
- procedure WEGetSelection (var selStart, selEnd: LongInt;
- hWE: WEHandle);
- var
- pWE: WEPtr;
- begin
- pWE := hWE^;
- selStart := pWE^.selStart;
- selEnd := pWE^.selEnd;
- end; { WEGetSelection }
-
- procedure WESetDestRect ({const} var destRect: LongRect;
- hWE: WEHandle);
- begin
- hWE^^.destRect := destRect;
- end; { WESetDestRect }
-
- procedure WEGetDestRect (var destRect: LongRect;
- hWE: WEHandle);
- begin
- destRect := hWE^^.destRect;
- end; { WEGetDestRect }
-
- procedure WESetViewRect ({const} var viewRect: LongRect;
- hWE: WEHandle);
- var
- pWE: WEPtr;
- r: Rect;
- begin
- pWE := hWE^;
- pWE^.viewRect := viewRect;
-
- { keep the viewRgn in sync with the view rectangle }
- WELongRectToRect(viewRect, r);
- RectRgn(pWE^.viewRgn, r);
-
- end; { WESetViewRect }
-
- procedure WEGetViewRect (var viewRect: LongRect;
- hWE: WEHandle);
- begin
- viewRect := hWE^^.viewRect;
- end; { WEGetViewRect }
-
- function WEGetTextLength (hWE: WEHandle): LongInt;
- begin
- WEGetTextLength := hWE^^.textLength;
- end; { WEGetTextLength }
-
- function WECountLines (hWE: WEHandle): LongInt;
- begin
- WECountLines := hWE^^.nLines;
- end; { WECountLines }
-
- function WEGetHeight (startLine, endLine: LongInt;
- hWE: WEHandle): LongInt;
- var
- pWE: WEPtr;
- pLines: LineArrayPtr;
- nLines: LongInt;
- begin
- pWE := hWE^;
- pLines := pWE^.hLines^;
- nLines := pWE^.nLines;
- startLine := _WEPinInRange(startLine, 0, nLines);
- endLine := _WEPinInRange(endLine, 0, nLines);
- _WEReorder(startLine, endLine);
- WEGetHeight := pLines^[endLine].lineOrigin - pLines^[startLine].lineOrigin;
- end; { WEGetHeight }
-
- function WEGetText (hWE: WEHandle): Handle;
- begin
- WEGetText := hWE^^.hText;
- end; { WEGetText }
-
- function WEGetChar (offset: LongInt;
- hWE: WEHandle): Char;
- var
- pWE: WEPtr;
- begin
- WEGetChar := Char(0);
- pWE := hWE^;
-
- { sanity check: make sure offset is withing allowed bounds }
- if ((offset < 0) or (offset >= pWE^.textLength)) then
- Exit(WEGetChar);
-
- { get the specified character (actually, byte) }
- WEGetChar := WECharsHandle(pWE^.hText)^^[offset];
-
- end; { WEGetChar }
-
- end.